﻿#include "dxstdafx.h"
#include "CStrategyRiverRender.h"
#include "CStrategyRenderCommon.h"
#include "../CStrategyDefine.h"

extern CStrategyRenderData g_StrategyRenderData;
extern CStrategyCamera g_Camera;

CStrategyRiverRender::CStrategyRiverRender()
{
	m_pDecl = NULL;
	m_pEffect = NULL;
}

CStrategyRiverRender::~CStrategyRiverRender()
{
	for (auto& it : m_listRiverVerticInfo)
	{
		SAFE_RELEASE(it.pVB);
		SAFE_RELEASE(it.pIB);
	}
	SAFE_RELEASE(m_pDecl);
	SAFE_RELEASE(m_pEffect);
}

HRESULT CStrategyRiverRender::OnCreateDevice(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;

	D3DVERTEXELEMENT9 decl[] =
	{
		{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
		{ 0, 12, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
		D3DDECL_END()
	};
	V_RETURN( pd3dDevice->CreateVertexDeclaration( decl, &m_pDecl ));

	LPD3DXBUFFER pCode = NULL;
	std::wstring szRcPathOut;
	CommonFunc::RelativePath2FullPath( L"\\ResFile\\fx\\Strategy\\StrategyRiverRender.fx", szRcPathOut );
	if ( FAILED( hr = D3DXCreateEffectFromFile( pd3dDevice, szRcPathOut.c_str(), NULL, NULL,
		0, NULL, &m_pEffect, &pCode )))
	{
		LPVOID pBufErr = pCode->GetBufferPointer();
		char* ErrInfo = (char*)pBufErr;
		pCode->Release();
		return hr;
	}
	CommonFunc::CheckFxWarning(pCode);

	return hr;
}

HRESULT CStrategyRiverRender::OnResetDevice(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	V_RETURN(CreateMeshWhenResetDevice(pd3dDevice));
	V_RETURN(m_pEffect->SetTechnique("RiverColor"));

	V_RETURN(m_pEffect->SetTexture("DiffuseSurface", g_StrategyRenderData.m_pTexRiverSurfaceDiffuse));
	V_RETURN(m_pEffect->SetTexture("NormalSurface", g_StrategyRenderData.m_pTexRiverSurfaceNormal));
	V_RETURN(m_pEffect->SetTexture("DiffuseBottom", g_StrategyRenderData.m_pTexRiverBottomDiffuse));
	V_RETURN(m_pEffect->SetTexture("NormalBottom", g_StrategyRenderData.m_pTexRiverBottomNormal));
	V_RETURN(m_pEffect->SetTexture("ColorOverlay", g_StrategyRenderData.m_pTexTerrainColorTint));
	V_RETURN(m_pEffect->SetTexture("HeightNormal", g_StrategyRenderData.m_pTexHeightNormal));

	V_RETURN(m_pEffect->SetVector("g_vMapSize_XZScale_YScale", &D3DXVECTOR4(c_nTerrainWidth, c_nTerrainHeight, c_fTerrainSqareScale, c_fHeightMapScale)));
	return hr;
}

void CStrategyRiverRender::OnFrameMove(const D3DXMATRIXA16* pWorldViewProject, const D3DXVECTOR4* vCamLookAtDir, double fTime)
{
	HRESULT hr = S_OK;
	V(m_pEffect->SetMatrix("g_matWorldViewProject", pWorldViewProject));
	V(m_pEffect->SetVector("g_vCamLookAtDir", vCamLookAtDir));
	V(m_pEffect->SetVector("g_vCamPos", (D3DXVECTOR4*)g_Camera.GetEyePt()));
	V(m_pEffect->SetVector("g_vLightDir", &c_vLightDir));
	V(m_pEffect->SetVector("g_vTimeDirection_Unused_Unused", &D3DXVECTOR4(fTime, -c_fRiverFlowSpeed, 0.f, 0.f)));
}

void CStrategyRiverRender::OnFrameRender(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	V(pd3dDevice->SetVertexDeclaration(m_pDecl));
	
	std::set<int> set_RenderSquare;
	if (!g_StrategyRenderData.GetRenderSquareSet(&set_RenderSquare))
	{
		return;
	}
	for (const auto& itInfo : m_listRiverVerticInfo)
	{
		bool bRender = CommonFunc::IsTwoSetHaveSameElement<int>(set_RenderSquare, itInfo.setAreaIn);		
		if (bRender)
		{
			V(pd3dDevice->SetStreamSource(0, itInfo.pVB, 0, sizeof(_stStrategyRiverD3DVertex)));
			V(pd3dDevice->SetIndices(itInfo.pIB));
			UINT iPass, cPasses;
			V(m_pEffect->Begin(&cPasses, 0));
			for (iPass = 0; iPass < cPasses; ++iPass)
			{
				V(m_pEffect->BeginPass(iPass));
				V(pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, itInfo.dwVerticesNum, 0, itInfo.dwIndicesNum - 2));
				V(m_pEffect->EndPass());
			}
			V(m_pEffect->End());
		}
	}
}

void CStrategyRiverRender::OnLostDevice()
{
	if (m_pEffect)
	{
		m_pEffect->OnLostDevice();
	}
	for (auto& it : m_listRiverVerticInfo)
	{
		SAFE_RELEASE(it.pVB);
		SAFE_RELEASE(it.pIB);
	}
}

void CStrategyRiverRender::OnDestroyDevice()
{
	for (auto& it : m_listRiverVerticInfo)
	{
		SAFE_RELEASE(it.pVB);
		SAFE_RELEASE(it.pIB);
	}
	SAFE_RELEASE(m_pEffect);
	SAFE_RELEASE(m_pDecl);
}

HRESULT CStrategyRiverRender::CreateMeshWhenResetDevice(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;

	if (m_listAllH2TRiverBody.empty())
	{
		std::wstring szRcPathOut;
		int nTerrainWidth, nTerrainHeight;

		CommonFunc::RelativePath2FullPath(L"\\ResFile\\Scene\\Strategy\\rivers.bmp", szRcPathOut);
		BYTE* pRiverBYTEBuf = nullptr;
		if (!CommonFunc::Read8BitBmpWithBYTE(szRcPathOut, pRiverBYTEBuf, &nTerrainWidth, &nTerrainHeight) || nTerrainWidth != c_nTerrainWidth || nTerrainHeight != c_nTerrainHeight)
		{
			CString strError("Read River Map Failure!");
			CommonFunc::ErrorMessageBox(strError);
			return -1;
		}
		m_pRiverBYTEBuf.reset(pRiverBYTEBuf);

		if (!PickRiverHead())
		{
			CString strErr(L"Read river map failure!");
			CommonFunc::ErrorMessageBox(strErr);
			return -1;
		}
		printf("Traverse Strategy RiverMap Success!\n");

		std::unordered_map<DWORD, D3DXVECTOR2> mapPointTrans;
		for (auto& it : m_listAllH2TRiverBody)
		{
			if (!CStrategyRenderFunc::RebuildBodyList(it, c_nRiverFuzzy, true, &mapPointTrans, 0.f, false))
			{
				return -1;
			}
		}
		m_pRiverBYTEBuf.reset();
	}
	
	V_RETURN(BuildMesh(pd3dDevice));
	printf("Create Strategy River Mesh Success!\n");

	return hr;
}

bool CStrategyRiverRender::PickRiverHead()
{
	for (int iH = 0; iH < c_nTerrainHeight; ++iH)
	{
		for (int jW = 0; jW < c_nTerrainWidth; ++jW)
		{
			if (m_pRiverBYTEBuf)
			{
				_eRiverKind sign;
				int index = CMathFunc::XYTran2Index(jW, iH, c_nTerrainWidth, c_nTerrainHeight);
				
				_stBodyInfo st;
				st.info = m_pRiverBYTEBuf[index];
				if (RS_NormalHead == st.info)
				{
					sign = RK_BodyBigger;
				}
				else if (RS_HeadSignSame == st.info)
				{
					sign = RK_BodySame;
				}
				else
				{
					continue;
				}
				st.vec2 = D3DXVECTOR2(jW, iH);
				
				_dequeRiverBody dequeRiverBody;
				dequeRiverBody.push_back(st);
				
				if (!TraverseRiver(st.vec2, D3DXVECTOR2(-1.f, -1.f), sign, dequeRiverBody))
				{
					return false;
				}
			}
		}
	}
	return true;
}

bool CStrategyRiverRender::TraverseRiver(D3DXVECTOR2 ptCenter, D3DXVECTOR2 ptStart, _eRiverKind sign, _dequeRiverBody& dequeRiverBody)
{
OptimizeTailRecursion:	// 手动优化尾递归
	if (!CStrategyRenderFunc::IsPointLegal(ptCenter))
	{
		CString strErr;
		strErr.Format(L"(%d, %d) is illegal!", ptCenter.x, ptCenter.y);
		CommonFunc::ErrorMessageBox(strErr);
		return false;
	}
	
	bool bHaveNextBody = false;
	_stBodyInfo stNextBody;

	for (int eDir = DirMin; eDir < DirMax; ++eDir)
	{
		D3DXVECTOR2 ptTemp;
		if (CStrategyRenderFunc::DirTrans2XY((_eDirULDR)eDir, ptCenter, ptTemp))
		{
			// ptTemp 合法
			if (ptTemp == ptStart)
			{
				continue;
			}

			int indexTemp = CMathFunc::XYTran2Index(ptTemp.x, ptTemp.y, c_nTerrainWidth, c_nTerrainHeight);

			if (RS_ForkSignSmaller == m_pRiverBYTEBuf[indexTemp])
			{
				_dequeRiverBody dequeRiverBodyNew;

				_stBodyInfo st;
				st.vec2 = ptCenter;
				DWORD indexC = CMathFunc::XYTran2Index(ptCenter.x, ptCenter.y, c_nTerrainWidth, c_nTerrainHeight);
				st.info = m_pRiverBYTEBuf[indexC];
				dequeRiverBodyNew.push_front(st);

				st.vec2 = ptTemp;
				st.info = RS_ForkSignSmaller;
				dequeRiverBodyNew.push_front(st);

				if (!TraverseRiver(ptTemp, ptCenter, RK_BodySmaller, dequeRiverBodyNew))
				{
					return false;
				}
			}
			else if (RS_ForkSignBigger == m_pRiverBYTEBuf[indexTemp])
			{
				_dequeRiverBody dequeRiverBodyNew;

				_stBodyInfo st;
				st.vec2 = ptCenter;
				DWORD indexC = CMathFunc::XYTran2Index(ptCenter.x, ptCenter.y, c_nTerrainWidth, c_nTerrainHeight);
				st.info = m_pRiverBYTEBuf[indexC];
				dequeRiverBodyNew.push_back(st);

				st.vec2 = ptTemp;
				st.info = RS_ForkSignBigger;
				dequeRiverBodyNew.push_back(st);

				if (!TraverseRiver(ptTemp, ptCenter, RK_BodyBigger, dequeRiverBodyNew))
				{
					return false;
				}
			}
			else if (!bHaveNextBody && IsRiverBody(ptTemp))
			{
				// 此时记录信息 不遍历,是为了主干在前 支干在后
				if (RK_BodySame == sign)
				{
					int indexCenter = CMathFunc::XYTran2Index(ptCenter.x, ptCenter.y, c_nTerrainWidth, c_nTerrainHeight);
					_eRiverSign eCenter = (_eRiverSign)m_pRiverBYTEBuf[indexCenter];
					if (IsRiverBody(eCenter) && eCenter != m_pRiverBYTEBuf[indexTemp])
					{
						continue;
					}
				}
				bHaveNextBody = true;
				stNextBody.vec2 = ptTemp;
				stNextBody.info = m_pRiverBYTEBuf[indexTemp];
			}
		}
	}

	if (bHaveNextBody)
	{
		switch (sign)
		{
		case RK_BodySmaller:
			dequeRiverBody.push_front(stNextBody);
			break;
		case RK_BodyBigger:
		case RK_BodySame:
			dequeRiverBody.push_back(stNextBody);
			break;
		default:
			{
				CString strErr;
				strErr.Format(L"_eRiverKind %d is illegal!", sign);
				CommonFunc::ErrorMessageBox(strErr);
				return false;
			}
			break;
		}
		ptStart = ptCenter; ptCenter = stNextBody.vec2;
		goto OptimizeTailRecursion;
		//return TraverseRiver(stNextBody.vec2, ptCenter, sign, dequeRiverBody);
	}
	else
	{
		if (dequeRiverBody.size() < 2)
		{
			CString strErr(L"dequeBody is too short!");
			CommonFunc::ErrorMessageBox(strErr);
			return false;
		}
		m_listAllH2TRiverBody.push_front(dequeRiverBody);	// 让主干在前，支干在后
	}
	
	return true;
}

bool CStrategyRiverRender::IsRiverBody(const D3DXVECTOR2& pt)
{
	if (!CStrategyRenderFunc::IsPointLegal(pt))
	{
		return false;
	}
	int index = CMathFunc::XYTran2Index(pt.x, pt.y, c_nTerrainWidth, c_nTerrainHeight);
	return IsRiverBody((_eRiverSign)m_pRiverBYTEBuf[index]);
}

bool CStrategyRiverRender::IsRiverBody(_eRiverSign e)
{
	return (RS_BodyMin <= e && e <= RS_BodyMax);
}

float CStrategyRiverRender::GetRiverHeight(float x, float y)
{
	return CStrategyRenderFunc::GetHeightByFloatXY(x, y) * c_fHeightMapScale;
}

float CStrategyRiverRender::GetRiverWidth(const _dequeRiverBody& deque, int index, bool bSameWidth)
{
	int nLength = (int)deque.size() - 1;
	if (index < 0 || index > nLength || nLength < 1)
	{
		return 0.f;
	}
	if (bSameWidth)
	{
		// 同宽度
		int nBackIndex = deque.back().info;
		if (!IsRiverBody((_eRiverSign)nBackIndex))
		{
			CString strErr;
			strErr.Format(L"(%f, %f)'s sign is wrong!", deque.back().vec2, deque.back().vec2);
			CommonFunc::ErrorMessageBox(strErr);
			return 0.f;
		}
		return (nBackIndex - RS_BodyMin + 1) * c_fRiverWidthN;
	}
	else
	{
		// 不同宽度
		int nBeginIndex = deque.front().info;
		if (!IsRiverBody((_eRiverSign)nBeginIndex))
		{
			nBeginIndex = deque[1].info;
		}
		int nBodyIndex = deque[index].info;
		int nBackIndex = deque.back().info;
		if (!IsRiverBody((_eRiverSign)nBodyIndex))
		{
			if (index < deque.size() / 2.f)
			{
				nBodyIndex = deque[index + 1].info;
			}
			else
			{
				nBodyIndex = deque[index - 1].info;
			}
		}
		if (!IsRiverBody((_eRiverSign)nBackIndex) || !IsRiverBody((_eRiverSign)nBodyIndex))
		{
			CString strErr;
			strErr.Format(L"There is a same sign around (%f, %f)!", deque[index].vec2.x, deque[index].vec2.y);
			CommonFunc::ErrorMessageBox(strErr);
			return 0.f;
		}
		return CMathFunc::Lerp((nBeginIndex - RS_BodyMin + 1) * c_fRiverWidthN, (nBackIndex - RS_BodyMin + 1) * c_fRiverWidthN, (float)index / nLength) + (nBodyIndex - RS_BodyMin + 1) * c_fRiverWidthN;
	}
}

HRESULT CStrategyRiverRender::BuildMesh(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	m_listRiverVerticInfo.clear();
	for (const auto &itHead : m_listAllH2TRiverBody)
	{
		int nSize = (int)itHead.size();
		if (nSize < 2)
		{
			CString strErr(L"River is too short!");
			CommonFunc::ErrorMessageBox(strErr);
			return -1;
		}
		_stStrategyRiverVerticesInfo st;
		bool bHeadSingle = (RS_NormalHead == itHead[0].info || RS_ForkSignSmaller == itHead[nSize - 2].info);
		bool bSameWidth = (RS_HeadSignSame == itHead[0].info);
		st.dwVerticesNum = (bHeadSingle) ? 2 * nSize - 1 : 2 * nSize;
		{
			DWORD indexVB = 0;
			_stStrategyRiverD3DVertex *pVertices;
			V_RETURN(pd3dDevice->CreateVertexBuffer(st.dwVerticesNum * sizeof(_stStrategyRiverD3DVertex),
				0, 0, D3DPOOL_DEFAULT, &st.pVB, NULL));
			V_RETURN(st.pVB->Lock(0, 0, (void**)&pVertices, 0));
			
			for (int i = 0; i < nSize; ++i)
			{
				D3DXVECTOR2 nVec90, nVec270, vOff;
				if (0 == i)
				{
					vOff = itHead[1].vec2 - itHead[0].vec2;
					if (bHeadSingle)
					{
						pVertices[indexVB].x = itHead[0].vec2.x;
						pVertices[indexVB].y = GetRiverHeight(itHead[0].vec2.x, itHead[0].vec2.y);
						pVertices[indexVB].z = itHead[0].vec2.y;
						pVertices[indexVB].u = 0.f;
						pVertices[indexVB].v = 0.5;
						pVertices[indexVB].Tangentx = vOff.x;
						pVertices[indexVB].Tangenty = vOff.y;
						st.setAreaIn.insert(CStrategyRenderFunc::GetRenderSquareIndex(itHead[0].vec2.x, itHead[0].vec2.y));
						++indexVB;
						continue;
					}
				}
				else if (nSize - 1 == i)
				{
					vOff = itHead[nSize - 1].vec2 - itHead[nSize - 2].vec2;
				}
				else
				{
					vOff = itHead[i + 1].vec2 - itHead[i - 1].vec2;
				}
				D3DXVec2Normalize(&nVec90, &CMathFunc::Vec2Rotate(vOff, D3DX_PI / 2.f));
				nVec270 = -nVec90;
				
				float fWidth = GetRiverWidth(itHead, i, bSameWidth);
					
				nVec90 *= fWidth / 2.f;
				nVec90 += itHead[i].vec2;
				
				pVertices[indexVB].x = nVec90.x;
				pVertices[indexVB].y = GetRiverHeight(nVec90.x, nVec90.y);
				pVertices[indexVB].z = nVec90.y;
				pVertices[indexVB].u = i;
				pVertices[indexVB].v = 1.f;
				pVertices[indexVB].Tangentx = vOff.x;
				pVertices[indexVB].Tangenty = vOff.y;
				st.setAreaIn.insert(CStrategyRenderFunc::GetRenderSquareIndex(nVec90.x, nVec90.y));
				++indexVB;
				
				nVec270 *= fWidth / 2.f;
				nVec270 += itHead[i].vec2;

				pVertices[indexVB].x = nVec270.x;
				pVertices[indexVB].y = GetRiverHeight(nVec270.x, nVec270.y);
				pVertices[indexVB].z = nVec270.y;
				pVertices[indexVB].u = i;
				pVertices[indexVB].v = 0.f;
				pVertices[indexVB].Tangentx = vOff.x;
				pVertices[indexVB].Tangenty = vOff.y;
				st.setAreaIn.insert(CStrategyRenderFunc::GetRenderSquareIndex(nVec270.x, nVec270.y));
				++indexVB;
			}
			V_RETURN(st.pVB->Unlock());
		}

		st.dwIndicesNum = st.dwVerticesNum;		// 三角形带 保证顺时针
		{
			DWORD* pIndices;
			V_RETURN(pd3dDevice->CreateIndexBuffer(st.dwIndicesNum * sizeof(DWORD),
				0, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &st.pIB, NULL));
			V_RETURN(st.pIB->Lock(0, 0, (void**)&pIndices, 0));
			
			if (bHeadSingle)
			{
				pIndices[0] = 0;
				for (DWORD i = 1; i < st.dwIndicesNum; i = i + 2)
				{
					pIndices[i] = i + 1;
				}
				for (DWORD i = 2; i < st.dwIndicesNum; i = i + 2)
				{
					pIndices[i] = i - 1;
				}
			}
			else
			{
				for (DWORD i = 0; i < st.dwIndicesNum; ++i)
				{
					pIndices[i] = i;
				}
			}
			
			V_RETURN(st.pIB->Unlock());
		}

		m_listRiverVerticInfo.push_back(st);
	}
	return hr;
}